




#ifdef TRANSFORM1

/*
 *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef __KIS_GRID_INTERPOLATION_TOOLS_H
#define __KIS_GRID_INTERPOLATION_TOOLS_H

#include <limits>
#include <algorithm>

#include <QImage>

//#include "kis_algebra_2d.h"
//#include "kis_four_point_interpolator_forward.h"
//#include "kis_four_point_interpolator_backward.h"
//#include "kis_iterator_ng.h"
//#include "kis_random_sub_accessor.h"

 //#define DEBUG_PAINTING_POLYGONS

#ifdef DEBUG_PAINTING_POLYGONS
#include <QPainter>
#endif /* DEBUG_PAINTING_POLYGONS */
#include <QVector2D>
#include <QPainter>
#include <iostream>
namespace KisAlgebra2D {

    template <class T>
    struct PointTypeTraits
    {
    };

    template <>
    struct PointTypeTraits<QPoint>
    {
        typedef int value_type;
        typedef qreal calculation_type;
        typedef QRect rect_type;
    };

    template <>
    struct PointTypeTraits<QPointF>
    {
        typedef qreal value_type;
        typedef qreal calculation_type;
        typedef QRectF rect_type;
    };


    template <class T>
    typename PointTypeTraits<T>::value_type dotProduct(const T& a, const T& b)
    {
        return a.x() * b.x() + a.y() * b.y();
    }

    template <class T>
    typename PointTypeTraits<T>::value_type crossProduct(const T& a, const T& b)
    {
        return a.x() * b.y() - a.y() * b.x();
    }
}


class KisFourPointInterpolatorBackward
{
public:
    KisFourPointInterpolatorBackward(const QPolygonF& srcPolygon, const QPolygonF& dstPolygon) {
        m_a = dstPolygon[1] - dstPolygon[0]; // AB
        m_b = dstPolygon[2] - dstPolygon[1]; // BD
        m_c = dstPolygon[3] - dstPolygon[0]; // AC
        m_d = m_b - m_c; // BD - AC

        m_qA = m_c.x() * m_d.y() - m_c.y() * m_d.x();

        m_srcBase = srcPolygon[0];
        m_dstBase = dstPolygon[0];
        m_xCoeff = srcPolygon[1].x() - srcPolygon[0].x(); // AB_src
        m_yCoeff = srcPolygon[3].y() - srcPolygon[0].y(); // AC_src

        m_qB_const = m_c.x() * m_a.y() - m_c.y() * m_a.x();

        m_qD_div = 1.0 / (2 * m_qA);

        //m_qB_varX = 0.0;
        //m_qB_varY = 0.0;
    }

    /**
     * Checks if linear dimensions of the destination polygon are
     * bigger than \p tolerance.
     */
    inline bool isValid(const qreal tolerance = 0.1) const {
        const qreal toleranceSq = tolerance * tolerance;

        const qreal sq1 = qAbs(m_qB_const);
        const qreal sq2 = qAbs(KisAlgebra2D::crossProduct(m_b, m_c - m_b + m_a));

        return sq1 + sq2 > 2 * toleranceSq;
    }

    inline QPointF fallbackSourcePoint() const {
        return m_srcBase + QPointF(0.5 * m_xCoeff, 0.5 * m_yCoeff);
    }

    inline QPointF map(const QPointF& pt) {
        setX(pt.x());
        setY(pt.y());
        return getValue();
    }

    inline void setX(qreal x) {
        x -= m_dstBase.x();

        m_qB_varX = -x * m_d.y();
        m_qC_varX = -x * m_a.y();
        m_px = x;
    }

    inline void setY(qreal y) {
        y -= m_dstBase.y();

        m_qB_varY = y * m_d.x();
        m_qC_varY = y * m_a.x();
        m_py = y;
    }

    inline QPointF getValue() const {
        static const qreal eps = 1e-10;

        qreal qB = m_qB_const + m_qB_varX + m_qB_varY;
        qreal qC = m_qC_varX + m_qC_varY;

        qreal nu = 0.0;

        if (qAbs(m_qA) < eps) {
            nu = -qC / qB;
            
        }
        else {
            qreal D = qB*qB - 4 * m_qA * qC;
            if (D > 0.0) {
                qreal sqrtD = std::sqrt(D);
                nu = (-qB - sqrtD) * m_qD_div;
                if (nu < 0.0 || nu > 1.0) {
                    qreal nu2 = (-qB + sqrtD) * m_qD_div;

                    if (nu2 < 0.0 || nu2 > 1.0) {
                        nu = qBound(qreal(0.0), nu, qreal(1.0));
                    }
                    else {
                        nu = nu2;
                    }
                }
            }
            else {
                nu = 0.0;
            }
        }

        qreal xBasedDenominator = m_a.x() + nu * m_d.x();

        qreal mu;

        if (qAbs(xBasedDenominator) > eps) {
            mu = (m_px - nu * m_c.x()) / xBasedDenominator;
        }
        else {
            mu = (m_py - nu * m_c.y()) / (m_a.y() + nu * m_d.y());
        }

        return m_srcBase + QPointF(mu * m_xCoeff, nu * m_yCoeff);
    }

private:
    QPointF m_a; // AB
    QPointF m_b; // BD
    QPointF m_c; // AC
    QPointF m_d; // m_b - m_c

    qreal m_qA{ 0.0 }; // quadratic equation A coeff
    qreal m_qB_const{ 0.0 }; // quadratic equation B coeff, const part
    qreal m_qB_varX{ 0.0 }; // quadratic equation B coeff, X-dep part
    qreal m_qB_varY{ 0.0 }; // quadratic equation B coeff, Y-dep part
    qreal m_qC_varX{ 0.0 }; // quadratic equation C coeff, X-dep part
    qreal m_qC_varY{ 0.0 }; // quadratic equation C coeff, Y-dep part
    qreal m_qD_div{ 0.0 }; // inverted divisor of the quadratic equation solution
    qreal m_px{ 0.0 }; // saved relative X coordinate
    qreal m_py{ 0.0 }; // saved relative Y coordinate

    QPointF m_srcBase;
    QPointF m_dstBase;
    qreal m_xCoeff{ 0.0 };
    qreal m_yCoeff{ 0.0 };
};




//void kis_assert_recoverable(const char* assertion, const char* file, int line)
//{
//    kis_assert_common(assertion, file, line, false, false);
//}
//
//#define KIS_ASSERT_RECOVER(cond) if (!(cond) && (kis_assert_recoverable(#cond,__FILE__,__LINE__), true))
//
//
//#define KIS_ASSERT_RECOVER_NOOP(cond) do { KIS_ASSERT_RECOVER(cond) { qt_noop(); } } while (0)



namespace GridIterationTools {

    inline int calcGridDimension(int start, int end, const int pixelPrecision)
    {
        const int alignmentMask = ~(pixelPrecision - 1);

        int alignedStart = (start + pixelPrecision - 1) & alignmentMask;
        int alignedEnd = end & alignmentMask;

        int size = 0;

        if (alignedEnd > alignedStart) {
            size = (alignedEnd - alignedStart) / pixelPrecision + 1;
            size += alignedStart != start;
            size += alignedEnd != end;
        }
        else {
            size = 2 + (end - start >= pixelPrecision);
        }

        return size;
    }

    inline QSize calcGridSize(const QRect& srcBounds, const int pixelPrecision) {
        return QSize(calcGridDimension(srcBounds.x(), srcBounds.right(), pixelPrecision),
            calcGridDimension(srcBounds.y(), srcBounds.bottom(), pixelPrecision));
    }

    template <class ProcessPolygon, class ForwardTransform>
    struct CellOp
    {
        CellOp(ProcessPolygon& _polygonOp, ForwardTransform& _transformOp)
            : polygonOp(_polygonOp),
            transformOp(_transformOp)
        {
        }

        inline void processPoint(int col, int row,
            int prevCol, int prevRow,
            int colIndex, int rowIndex) {

            QPointF dstPosF = transformOp(QPointF(col, row));
            currLinePoints << dstPosF;

            if (rowIndex >= 1 && colIndex >= 1) {
                QPolygonF srcPolygon;

                srcPolygon << QPointF(prevCol, prevRow);
                srcPolygon << QPointF(col, prevRow);
                srcPolygon << QPointF(col, row);
                srcPolygon << QPointF(prevCol, row);

                QPolygonF dstPolygon;

                dstPolygon << prevLinePoints.at(colIndex - 1);
                dstPolygon << prevLinePoints.at(colIndex);
                dstPolygon << currLinePoints.at(colIndex);
                dstPolygon << currLinePoints.at(colIndex - 1);

                polygonOp(srcPolygon, dstPolygon);
            }

        }

        inline void nextLine() {
            std::swap(prevLinePoints, currLinePoints);

            // we are erasing elements for not freeing the occupied
            // memory, which is more efficient since we are going to fill
            // the vector again
            currLinePoints.erase(currLinePoints.begin(), currLinePoints.end());
        }

        QVector<QPointF> prevLinePoints;
        QVector<QPointF> currLinePoints;
        ProcessPolygon& polygonOp;
        ForwardTransform& transformOp;
    };

    template <class ProcessCell>
    void processGrid(ProcessCell& cellOp,
        const QRect& srcBounds,
        const int pixelPrecision)
    {
        if (srcBounds.isEmpty()) return;

        const int alignmentMask = ~(pixelPrecision - 1);

        int prevRow = std::numeric_limits<int>::max();
        int prevCol = std::numeric_limits<int>::max();

        int rowIndex = 0;
        int colIndex = 0;

        for (int row = srcBounds.top(); row <= srcBounds.bottom();) {
            for (int col = srcBounds.left(); col <= srcBounds.right();) {

                cellOp.processPoint(col, row,
                    prevCol, prevRow,
                    colIndex, rowIndex);

                prevCol = col;
                col += pixelPrecision;
                colIndex++;

                if (col > srcBounds.right() &&
                    col <= srcBounds.right() + pixelPrecision - 1) {

                    col = srcBounds.right();
                }
                else {
                    col &= alignmentMask;
                }
            }

            cellOp.nextLine();
            colIndex = 0;

            prevRow = row;
            row += pixelPrecision;
            rowIndex++;

            if (row > srcBounds.bottom() &&
                row <= srcBounds.bottom() + pixelPrecision - 1) {

                row = srcBounds.bottom();
            }
            else {
                row &= alignmentMask;
            }
        }
    }

    template <class ProcessPolygon, class ForwardTransform>
    void processGrid(ProcessPolygon& polygonOp, ForwardTransform& transformOp,
        const QRect& srcBounds, const int pixelPrecision)
    {
        CellOp<ProcessPolygon, ForwardTransform> cellOp(polygonOp, transformOp);
        processGrid(cellOp, srcBounds, pixelPrecision);
    }


    struct QImagePolygonOp
    {
        QImagePolygonOp(const QImage& srcImage, QImage& dstImage,
            const QPointF& srcImageOffset,
            const QPointF& dstImageOffset)
            : m_srcImage(srcImage), m_dstImage(dstImage),
            m_srcImageOffset(srcImageOffset),
            m_dstImageOffset(dstImageOffset),
            m_srcImageRect(m_srcImage.rect()),
            m_dstImageRect(m_dstImage.rect())
        {
        }

        void operator() (const QPolygonF& srcPolygon, const QPolygonF& dstPolygon) {
            this->operator() (srcPolygon, dstPolygon, dstPolygon);
        }

        void operator() (const QPolygonF& srcPolygon, const QPolygonF& dstPolygon, const QPolygonF& clipDstPolygon) {
            QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
            KisFourPointInterpolatorBackward interp(srcPolygon, dstPolygon);

            for (int y = boundRect.top(); y <= boundRect.bottom(); y++) {
                interp.setY(y);
                for (int x = boundRect.left(); x <= boundRect.right(); x++) {

                    QPointF srcPoint(x, y);
                    if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {

                        interp.setX(srcPoint.x());
                        QPointF dstPoint = interp.getValue();

                        // about srcPoint/dstPoint hell please see a
                        // comment in PaintDevicePolygonOp::operator() ()

                        srcPoint -= m_dstImageOffset;
                        dstPoint -= m_srcImageOffset;

                        QPoint srcPointI = srcPoint.toPoint();
                        QPoint dstPointI = dstPoint.toPoint();

                        if (!m_dstImageRect.contains(srcPointI)) continue;
                        if (!m_srcImageRect.contains(dstPointI)) continue;

                        m_dstImage.setPixel(srcPointI, m_srcImage.pixel(dstPointI));
                    }
                }
            }

#ifdef DEBUG_PAINTING_POLYGONS
            QPainter gc(&m_dstImage);
            gc.setPen(Qt::red);
            gc.setOpacity(0.5);

            gc.setBrush(Qt::green);
            gc.drawPolygon(clipDstPolygon.translated(-m_dstImageOffset));

            gc.setBrush(Qt::blue);
            //gc.drawPolygon(dstPolygon.translated(-m_dstImageOffset));

#endif /* DEBUG_PAINTING_POLYGONS */

        }

        const QImage& m_srcImage;
        QImage& m_dstImage;
        QPointF m_srcImageOffset;
        QPointF m_dstImageOffset;

        QRect m_srcImageRect;
        QRect m_dstImageRect;
    };

    /*************************************************************/
    /*      Iteration through precalculated grid                 */
    /*************************************************************/

    /**
     *    A-----B         The polygons will be in the following order:
     *    |     |
     *    |     |         polygon << A << B << D << C;
     *    C-----D
     */
    inline QVector<int> calculateCellIndexes(int col, int row, const QSize& gridSize)
    {
        const int tl = col + row * gridSize.width();
        const int tr = tl + 1;
        const int bl = tl + gridSize.width();
        const int br = bl + 1;

        QVector<int> cellIndexes;
        cellIndexes << tl;
        cellIndexes << tr;
        cellIndexes << br;
        cellIndexes << bl;

        return cellIndexes;
    }

    inline int pointToIndex(const QPoint& cellPt, const QSize& gridSize)
    {
        return cellPt.x() +
            cellPt.y() * gridSize.width();
    }

    namespace Private {
        inline QPoint pointPolygonIndexToColRow(QPoint baseColRow, int index)
        {
            static QVector<QPoint> pointOffsets;
            if (pointOffsets.isEmpty()) {
                pointOffsets << QPoint(0, 0);
                pointOffsets << QPoint(1, 0);
                pointOffsets << QPoint(1, 1);
                pointOffsets << QPoint(0, 1);
            }

            return baseColRow + pointOffsets[index];
        }

        struct PointExtension {
            int near;
            int far;
        };
    }

    template <class IndexesOp>
    bool getOrthogonalPointApproximation(const QPoint& cellPt,
        const QVector<QPointF>& originalPoints,
        const QVector<QPointF>& transformedPoints,
        IndexesOp indexesOp,
        QPointF* srcPoint,
        QPointF* dstPoint)
    {
        QVector<Private::PointExtension> extensionPoints;
        Private::PointExtension ext;

        // left
        if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 0))) >= 0 &&
            (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 0))) >= 0) {

            extensionPoints << ext;
        }
        // top
        if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -1))) >= 0 &&
            (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, -2))) >= 0) {

            extensionPoints << ext;
        }
        // right
        if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 0))) >= 0 &&
            (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 0))) >= 0) {

            extensionPoints << ext;
        }
        // bottom
        if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 1))) >= 0 &&
            (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(0, 2))) >= 0) {

            extensionPoints << ext;
        }

        if (extensionPoints.isEmpty()) {
            // top-left
            if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, -1))) >= 0 &&
                (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, -2))) >= 0) {

                extensionPoints << ext;
            }
            // top-right
            if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, -1))) >= 0 &&
                (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, -2))) >= 0) {

                extensionPoints << ext;
            }
            // bottom-right
            if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(1, 1))) >= 0 &&
                (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(2, 2))) >= 0) {

                extensionPoints << ext;
            }
            // bottom-left
            if ((ext.near = indexesOp.tryGetValidIndex(cellPt + QPoint(-1, 1))) >= 0 &&
                (ext.far = indexesOp.tryGetValidIndex(cellPt + QPoint(-2, 2))) >= 0) {

                extensionPoints << ext;
            }
        }

        if (extensionPoints.isEmpty()) {
            return false;
        }

        int numResultPoints = 0;
        *srcPoint = indexesOp.getSrcPointForce(cellPt);
        *dstPoint = QPointF();

        Q_FOREACH(const Private::PointExtension & ext, extensionPoints) {
            QPointF near = transformedPoints[ext.near];
            QPointF far = transformedPoints[ext.far];

            QPointF nearSrc = originalPoints[ext.near];
            QPointF farSrc = originalPoints[ext.far];

            QPointF base1 = nearSrc - farSrc;
            QPointF base2 = near - far;

            QPointF pt = near +
                KisAlgebra2D::transformAsBase(*srcPoint - nearSrc, base1, base2);

            *dstPoint += pt;
            numResultPoints++;
        }

        *dstPoint /= numResultPoints;

        return true;
    }

    template <class PolygonOp, class IndexesOp>
    struct IncompletePolygonPolicy {

        static inline bool tryProcessPolygon(int col, int row,
            int numExistingPoints,
            PolygonOp& polygonOp,
            IndexesOp& indexesOp,
            const QVector<int>& polygonPoints,
            const QVector<QPointF>& originalPoints,
            const QVector<QPointF>& transformedPoints)
        {
            if (numExistingPoints >= 4) return false;
            if (numExistingPoints == 0) return true;

            QPolygonF srcPolygon;
            QPolygonF dstPolygon;

            for (int i = 0; i < 4; i++) {
                const int index = polygonPoints[i];

                if (index >= 0) {
                    srcPolygon << originalPoints[index];
                    dstPolygon << transformedPoints[index];
                }
                else {
                    QPoint cellPt = Private::pointPolygonIndexToColRow(QPoint(col, row), i);
                    QPointF srcPoint;
                    QPointF dstPoint;
                    bool result =
                        getOrthogonalPointApproximation(cellPt,
                            originalPoints,
                            transformedPoints,
                            indexesOp,
                            &srcPoint,
                            &dstPoint);

                    if (!result) {
                        //dbgKrita << "*NOT* found any valid point" << allSrcPoints[pointToIndex(cellPt)] << "->" << ppVar(pt);
                        break;
                    }
                    else {
                        srcPolygon << srcPoint;
                        dstPolygon << dstPoint;
                    }
                }
            }

            if (dstPolygon.size() == 4) {
                QPolygonF srcClipPolygon(srcPolygon.intersected(indexesOp.srcCropPolygon()));

                KisFourPointInterpolatorForward forwardTransform(srcPolygon, dstPolygon);
                for (int i = 0; i < srcClipPolygon.size(); i++) {
                    const QPointF newPt = forwardTransform.map(srcClipPolygon[i]);
                    srcClipPolygon[i] = newPt;
                }

                polygonOp(srcPolygon, dstPolygon, srcClipPolygon);
            }

            return true;
        }
    };

    template <class PolygonOp, class IndexesOp>
    struct AlwaysCompletePolygonPolicy {

        static inline bool tryProcessPolygon(int col, int row,
            int numExistingPoints,
            PolygonOp& polygonOp,
            IndexesOp& indexesOp,
            const QVector<int>& polygonPoints,
            const QVector<QPointF>& originalPoints,
            const QVector<QPointF>& transformedPoints)
        {
            Q_UNUSED(col);
            Q_UNUSED(row);
            Q_UNUSED(polygonOp);
            Q_UNUSED(indexesOp);
            Q_UNUSED(polygonPoints);
            Q_UNUSED(originalPoints);
            Q_UNUSED(transformedPoints);

            KIS_ASSERT_RECOVER_NOOP(numExistingPoints == 4);
            return false;
        }
    };

    struct RegularGridIndexesOp {

        RegularGridIndexesOp(const QSize& gridSize)
            : m_gridSize(gridSize)
        {
        }

        inline QVector<int> calculateMappedIndexes(int col, int row,
            int* numExistingPoints) const {

            *numExistingPoints = 4;
            QVector<int> cellIndexes =
                GridIterationTools::calculateCellIndexes(col, row, m_gridSize);

            return cellIndexes;
        }

        inline int tryGetValidIndex(const QPoint& cellPt) const {
            Q_UNUSED(cellPt);

           // KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable");
            return -1;
        }

        inline QPointF getSrcPointForce(const QPoint& cellPt) const {
            Q_UNUSED(cellPt);

           //KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable");
            return QPointF();
        }

        inline const QPolygonF srcCropPolygon() const {
          //  KIS_ASSERT_RECOVER_NOOP(0 && "Not applicable");
            return QPolygonF();
        }

        QSize m_gridSize;
    };

    /**
     * There is a weird problem in fetching correct bounds of the polygon.
     * If the rightmost (bottommost) point of the polygon is integral, then
     * QRectF() will end exactly on it, but when converting into QRect the last
     * point will not be taken into account. It happens due to the difference
     * between center-point/topleft-point point representation. In many cases
     * the latter is expected, but we don't work with it in Qt/Krita.
     */
    inline void adjustAlignedPolygon(QPolygonF& polygon)
    {
        static const qreal eps = 1e-5;
        static const  QPointF p1(eps, 0.0);
        static const  QPointF p2(eps, eps);
        static const  QPointF p3(0.0, eps);

        polygon[1] += p1;
        polygon[2] += p2;
        polygon[3] += p3;
    }

    template <template <class PolygonOp, class IndexesOp> class IncompletePolygonPolicy,
        class PolygonOp,
        class IndexesOp>
        void iterateThroughGrid(PolygonOp& polygonOp,
            IndexesOp& indexesOp,
            const QSize& gridSize,
            const QVector<QPointF>& originalPoints,
            const QVector<QPointF>& transformedPoints)
    {
        QVector<int> polygonPoints(4);

        for (int row = 0; row < gridSize.height() - 1; row++) {
            for (int col = 0; col < gridSize.width() - 1; col++) {
                int numExistingPoints = 0;

                polygonPoints = indexesOp.calculateMappedIndexes(col, row, &numExistingPoints);

                if (!IncompletePolygonPolicy<PolygonOp, IndexesOp>::
                    tryProcessPolygon(col, row,
                        numExistingPoints,
                        polygonOp,
                        indexesOp,
                        polygonPoints,
                        originalPoints,
                        transformedPoints)) {

                    QPolygonF srcPolygon;
                    QPolygonF dstPolygon;

                    for (int i = 0; i < 4; i++) {
                        const int index = polygonPoints[i];
                        srcPolygon << originalPoints[index];
                        dstPolygon << transformedPoints[index];
                    }

                    adjustAlignedPolygon(srcPolygon);
                    adjustAlignedPolygon(dstPolygon);

                    polygonOp(srcPolygon, dstPolygon);
                }
            }
        }
    }

}

#endif /* __KIS_GRID_INTERPOLATION_TOOLS_H */





typedef QPointF(*WarpMathFunction)(QPointF, QVector<QPointF>, QVector<QPointF>, qreal);

struct FunctionTransformOp
{
    FunctionTransformOp(WarpMathFunction function,
        const QVector<QPointF>& p,
        const QVector<QPointF>& q,
        qreal alpha)
        : m_function(function),
        m_p(p),
        m_q(q),
        m_alpha(alpha)
    {
    }

    QPointF operator() (const QPointF& pt) const {
        return m_function(pt, m_p, m_q, m_alpha);
    }

    WarpMathFunction m_function;
    const QVector<QPointF>& m_p;
    const QVector<QPointF>& m_q;
    qreal m_alpha;
};





QPointF rigidTransformMath(QPointF v, QVector<QPointF> p, QVector<QPointF> q, qreal alpha)
{
    int nbPoints = p.size();
    QVarLengthArray<qreal> w(nbPoints);
    qreal sumWi = 0;
    QPointF pStar(0, 0), qStar(0, 0);
    QVarLengthArray<QPointF> pHat(nbPoints), qHat(nbPoints);

    for (int i = 0; i < nbPoints; ++i) {
        if (v == p[i])
            return q[i];

        QVector2D tmp(p[i] - v);
        w[i] = 1. / pow(tmp.lengthSquared(), alpha);
        pStar += w[i] * p[i];
        qStar += w[i] * q[i];
        sumWi += w[i];
    }
    pStar /= sumWi;
    qStar /= sumWi;

    QVector2D res_tmp(0, 0);
    qreal qx, qy, px, py;
    for (int i = 0; i < nbPoints; ++i) {
        pHat[i] = p[i] - pStar;
        qHat[i] = q[i] - qStar;

        qx = w[i] * qHat[i].x();
        qy = w[i] * qHat[i].y();
        px = pHat[i].x();
        py = pHat[i].y();

        res_tmp += QVector2D(qx * px + qy * py, qx * py - qy * px);
    }

    QPointF f_arrow(res_tmp.normalized().toPointF());
    QVector2D v_m_pStar(v - pStar);
    QPointF res(f_arrow.x() * v_m_pStar.x() + f_arrow.y() * v_m_pStar.y(), f_arrow.x() * v_m_pStar.y() - f_arrow.y() * v_m_pStar.x());
    res += qStar;

    return res;
}




QImage transformQImage(
    const QVector<QPointF>& origPoint,
    const QVector<QPointF>& transfPoint,
    qreal alpha,
    const QImage& srcImage,
    const QPointF& srcQImageOffset,
    QPointF* newOffset)
{
    //if (srcImage.format() == QImage::Format_ARGB32) {
    //    return QImage();
    //}


    if (origPoint.size() == 1) {
        *newOffset = srcQImageOffset + (transfPoint[0] - origPoint[0]).toPoint();
        return srcImage;
    }

    WarpMathFunction warpMathFunction = &rigidTransformMath;
    FunctionTransformOp functionOp(warpMathFunction, origPoint, transfPoint, alpha);

    const QRectF srcBounds = QRectF(srcQImageOffset, srcImage.size());
    QRectF dstBounds;

    {
        QPolygonF testPoints;
        testPoints << srcBounds.topLeft();
        testPoints << srcBounds.topRight();
        testPoints << srcBounds.bottomRight();
        testPoints << srcBounds.bottomLeft();
        testPoints << srcBounds.topLeft();

        QPolygonF::iterator it = testPoints.begin() + 1;

        while (it != testPoints.end()) {
            it = testPoints.insert(it, 0.5 * (*it + *(it - 1)));
            it += 2;
        }

        it = testPoints.begin();

        while (it != testPoints.end()) {
            *it = functionOp(*it);
            ++it;
        }

        dstBounds = testPoints.boundingRect();
    }

    QPointF dstQImageOffset = dstBounds.topLeft();
    *newOffset = dstQImageOffset;

    QRect dstBoundsI = dstBounds.toAlignedRect();
    QImage dstImage(dstBoundsI.size(), srcImage.format());
    dstImage.fill(0);

    const int pixelPrecision = 32;
    GridIterationTools::QImagePolygonOp polygonOp(srcImage, dstImage, srcQImageOffset, dstQImageOffset);
    GridIterationTools::processGrid(polygonOp, functionOp, srcBounds.toAlignedRect(), pixelPrecision);

    dstImage.save("d://temp//transform.png");
    return dstImage;
}

#endif
























/*
 *  kis_warptransform_worker.cc -- part of Krita
 *
 *  Copyright (c) 2010 Marc Pegon <pe.marc@free.fr>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <limits>
#include <algorithm>

#include <QImage>

 //#include "kis_algebra_2d.h"
 //#include "kis_four_point_interpolator_forward.h"
 //#include "kis_four_point_interpolator_backward.h"
 //#include "kis_iterator_ng.h"
 //#include "kis_random_sub_accessor.h"

  //#define DEBUG_PAINTING_POLYGONS

#ifdef DEBUG_PAINTING_POLYGONS
#include <QPainter>
#endif /* DEBUG_PAINTING_POLYGONS */
#include <QVector2D>
#include <QVector>
#include <QPoint>
#include <iostream>
#include <QPainter>







#include "kis_grid_interpolation_tools.h"

#include <unordered_map>
#include <vector>


std::unordered_map<int, std::vector<int>> point_fx;


QPointF rigidTransformMath(QPointF v, QVector<QPointF> p, QVector<QPointF> q, qreal alpha)
{

    int nbPoints = p.size();
    QVarLengthArray<qreal> w(nbPoints);
    qreal sumWi = 0;
    QPointF pStar(0, 0), qStar(0, 0);
    QVarLengthArray<QPointF> pHat(nbPoints), qHat(nbPoints);

    for (int i = 0; i < nbPoints; ++i) {
        if (v == p[i])
            return q[i];

        QVector2D tmp(p[i] - v);
        w[i] = 1. / pow(tmp.lengthSquared(), alpha);
        pStar += w[i] * p[i];
        qStar += w[i] * q[i];
        sumWi += w[i];
    }
    pStar /= sumWi;
    qStar /= sumWi;

    QVector2D res_tmp(0, 0);
    qreal qx, qy, px, py;
    for (int i = 0; i < nbPoints; ++i) {
        pHat[i] = p[i] - pStar;
        qHat[i] = q[i] - qStar;

        qx = w[i] * qHat[i].x();
        qy = w[i] * qHat[i].y();
        px = pHat[i].x();
        py = pHat[i].y();

        res_tmp += QVector2D(qx * px + qy * py, qx * py - qy * px);
    }

    QPointF f_arrow(res_tmp.normalized().toPointF());
    QVector2D v_m_pStar(v - pStar);
    QPointF res(f_arrow.x() * v_m_pStar.x() + f_arrow.y() * v_m_pStar.y(), f_arrow.x() * v_m_pStar.y() - f_arrow.y() * v_m_pStar.x());
    res += qStar;

    return res;

}



QPointF inverseRigidTransformMath(QPointF v, QVector<QPointF> p, QVector<QPointF> q, qreal alpha)
{

    int nbPoints = p.size();
    QVarLengthArray<qreal> w(nbPoints);
    qreal sumWi = 0;
    QPointF pStar(0, 0), qStar(0, 0);
    QVarLengthArray<QPointF> pHat(nbPoints), qHat(nbPoints);

    for (int i = 0; i < nbPoints; ++i) {
        if (v == p[i])
            return q[i];

        QVector2D tmp(p[i] - v);
        w[i] = 1. / pow(tmp.lengthSquared(), alpha);
        pStar += w[i] * p[i];
        qStar += w[i] * q[i];
        sumWi += w[i];
    }
    pStar /= sumWi;
    qStar /= sumWi;

    QVector2D res_tmp(0, 0);
    qreal qx, qy, px, py;
    for (int i = 0; i < nbPoints; ++i) {
        pHat[i] = p[i] - pStar;
        qHat[i] = q[i] - qStar;

        qx = w[i] * qHat[i].x();
        qy = w[i] * qHat[i].y();
        px = pHat[i].x();
        py = pHat[i].y();

        res_tmp += QVector2D(qx * px + qy * py, qx * py - qy * px);
    }

    QPointF f_arrow(res_tmp.normalized().toPointF());
    QVector2D v_m_pStar(v - qStar);
    QPointF res(f_arrow.x() * v_m_pStar.x() + f_arrow.y() * v_m_pStar.y(), f_arrow.x() * v_m_pStar.y() - f_arrow.y() * v_m_pStar.x());
    res += qStar;

    return res;

}



typedef QPointF(*WarpMathFunction)(QPointF, QVector<QPointF>, QVector<QPointF>, qreal);

struct FunctionTransformOp
{
    FunctionTransformOp(WarpMathFunction function,
        const QVector<QPointF>& p,
        const QVector<QPointF>& q,
        qreal alpha)
        : m_function(function),
        m_p(p),
        m_q(q),
        m_alpha(alpha)
    {
    }

    QPointF operator() (const QPointF& pt) const {
        return m_function(pt, m_p, m_q, m_alpha);
    }

    WarpMathFunction m_function;
    const QVector<QPointF>& m_p;
    const QVector<QPointF>& m_q;
    qreal m_alpha;
};


QImage transformQImage(
    const QVector<QPointF>& origPoint,
    const QVector<QPointF>& transfPoint,
    qreal alpha,
    const QImage& srcImage,
    const QPointF& srcQImageOffset,
    QPointF* newOffset)
{

    WarpMathFunction warpMathFunction = &rigidTransformMath;


    warpMathFunction = &rigidTransformMath;


    if (!warpMathFunction ||
        origPoint.isEmpty() ||
        origPoint.size() != transfPoint.size()) {

        return srcImage;
    }

    if (origPoint.size() == 1) {
        *newOffset = srcQImageOffset + (transfPoint[0] - origPoint[0]).toPoint();
        return srcImage;
    }

    FunctionTransformOp functionOp(warpMathFunction, origPoint, transfPoint, alpha);

    const QRectF srcBounds = QRectF(srcQImageOffset, srcImage.size());
    QRectF dstBounds;

    {
        QPolygonF testPoints;
        //testPoints << srcBounds.topLeft();
        //testPoints << srcBounds.topRight();
        //testPoints << srcBounds.bottomRight();
        //testPoints << srcBounds.bottomLeft();
        //testPoints << srcBounds.topLeft();



        //QPolygonF::iterator it = testPoints.begin() + 1;

        //while (it != testPoints.end()) {
        //    it = testPoints.insert(it, 0.5 * (*it + *(it - 1)));
        //    it += 2;
        //}


        int step = 4;
        for (int i = 0; i < srcImage.width(); i = i + step) {
            
            for (int j = 0; j < srcImage.height(); j = j + step) {
                testPoints << QPointF(i, j);
          
            }
        }

        QPolygonF::iterator it = testPoints.begin();
         //it = testPoints.begin();

        while (it != testPoints.end()) {
            *it = functionOp(*it);
            ++it;
        }

        dstBounds = testPoints.boundingRect();

        
    }

    QPointF dstQImageOffset = dstBounds.topLeft();
    *newOffset =  dstQImageOffset;

    QRect dstBoundsI = dstBounds.toAlignedRect();
    QImage dstImage(dstBoundsI.size(), srcImage.format());
    dstImage.fill(0);

    const int pixelPrecision = 32;
    GridIterationTools::QImagePolygonOp polygonOp(srcImage, dstImage, srcQImageOffset, dstQImageOffset);
    dstImage.save("d://temp//transform1.png");
    GridIterationTools::processGrid(polygonOp, functionOp, srcBounds.toAlignedRect(), pixelPrecision);
    dstImage.save("d://temp//transform2.png");
    int d = 1;
    return dstImage;
}





#include "KisBezierTransformMesh.h"




void mesh_patch() {


    QImage srcImage("d://temp//people.png");
    //QImage srcImage("d://temp//bezier_size.png");

    KisBezierTransformMesh my_mesh(QRect(0, 0, srcImage.width(), srcImage.height()), QSize(3, 3));
   
    //QImage dstImage(srcImage.size(), srcImage.format());

    KisBezierMesh::ControlPointIndex indexx = my_mesh.hitTestControlPoint(QPoint(1920, 0), 1);
   // KisBezierMesh::ControlPointIndex index = my_mesh.hitTestControlPoint(QPoint(0, 0), 1);
    auto itrs = my_mesh.beginControlPoints();
    int cnt = 1;
    while (itrs != my_mesh.endControlPoints()) {
        itrs++;
        auto lva = *itrs;
        cnt++;
    }
    
    KisBezierMesh::ControlPointIndex index = (itrs).controlIndex();
    
    itrs--;
    index = (itrs).controlIndex();
    //移动一个点
    bool  isvalid = my_mesh.isIndexValid(index);
    KisBezierMeshDetails::smartMoveControl(my_mesh, index, QPoint(300, 300), KisBezierMeshDetails::SmartMoveMeshControlMode::MoveFree, 0);
    itrs--;

    index = (itrs).controlIndex();
    


    //再移动一个点
    KisBezierMeshDetails::smartMoveControl(my_mesh, index, QPoint(300,600), KisBezierMeshDetails::SmartMoveMeshControlMode::MoveFree, 0 );


    const QRect dstImageRect = my_mesh.dstBoundingRect().toAlignedRect();
    QImage dstImage(dstImageRect.size(), srcImage.format());
    auto itr = my_mesh.beginPatches();
    while (itr != my_mesh.endPatches()) {
        KisBezierPatch  patch = *itr;
        itr++;
        {
            //QImage dstImage(dstBoundsI.size(), srcImage.format());
          //  QImage dstImage(srcImage.size(), srcImage.format());
           // dstImage.fill(0);
            QRect rect = dstImage.rect();
            const QPoint srcQImageOffset;
            const QPoint dstQImageOffset;

            KisBezierTransformMesh::transformPatch(patch,
                srcQImageOffset, srcImage,
                dstQImageOffset, &dstImage);
            dstImage.save("d://temp//bezier_patch.png");
        }
    }

}




void move_patch() {


    using KisAlgebra2D::linearReshapeFunc;
    using Mesh = KisBezierTransformMesh;

    QImage srcImage("d://temp//people.png");
    KisBezierTransformMesh mesh(QRect(0, 0, srcImage.width(), srcImage.height()), QSize(3, 3));
    //KisBezierTransformMesh& mesh = *m_d->currentArgs.meshTransform();
   // mesh = m_d->initialMeshState;


   // KisBezierPatch *patchIt;
    //QPointF offset = pt - m_d->mouseClickPos;
    QPointF offset(0, -300) ;
    QPointF mouseClickPos(1500, 100);
    QPointF localPatchPos;



    auto hoveredPatch = mesh.hitTestPatch(mouseClickPos, &localPatchPos);
    auto patchIt = mesh.find(hoveredPatch);//注意查看这个值，使用了-> 和.操作符

    auto offsetSegment =
        [&](KisBezierTransformMesh::segment_iterator it,
            qreal t,
            const QPointF& offset) {

                QPointF offsetP1;
                QPointF offsetP2;

                std::tie(offsetP1, offsetP2) =
                    KisBezierUtils::offsetSegment(t, offset);


                smartMoveControl(mesh, it.itP1().controlIndex(), offsetP1, KisSmartMoveMeshControlMode::MoveSymmetricLock,0);
                smartMoveControl(mesh, it.itP2().controlIndex(), offsetP2, KisSmartMoveMeshControlMode::MoveSymmetricLock,0);
    };


    const QPointF center = patchIt->localToGlobal(QPointF(0.5, 0.5));
    const qreal centerDistance = kisDistance(mouseClickPos, center);

    KisBezierTransformMesh::segment_iterator nearestSegment = mesh.endSegments();
    qreal nearestSegmentSignificance = 0;
    qreal nearestSegmentDistance = std::numeric_limits<qreal>::max();
    qreal nearestSegmentDistanceSignificance = 0.0;
    qreal nearestSegmentParam = 0.5;

    auto testSegment =
        [&nearestSegment,
        &nearestSegmentSignificance,
        &nearestSegmentDistance,
        &nearestSegmentDistanceSignificance,
        &nearestSegmentParam,
        centerDistance, mouseClickPos, &mesh] (KisBezierTransformMesh::segment_iterator it, qreal param) {

        const QPointF movedPoint = KisBezierUtils::bezierCurve(it.p0(), it.p1(), it.p2(), it.p3(), param);
        const qreal distance = kisDistance(mouseClickPos, movedPoint);

        if (distance < nearestSegmentDistance) {
            const qreal proportion = KisBezierUtils::curveProportionByParam(it.p0(), it.p1(), it.p2(), it.p3(), param, 0.1);

            qreal distanceSignificance =
                centerDistance / (centerDistance + distance);

            if (distanceSignificance > 0.6) {
                distanceSignificance = std::min(1.0, linearReshapeFunc(distanceSignificance, 0.6, 0.75, 0.6, 1.0));
            }

            const qreal directionSignificance =
                1.0 - std::min(1.0, std::abs(proportion - 0.5) / 0.4);
            
            nearestSegmentDistance = distance;
            nearestSegment = it;
            nearestSegmentParam = param;
            //nearestSegmentSignificance = m_d->mode != Private::OVER_PATCH_LOCKED ? distanceSignificance * directionSignificance : 0;
            nearestSegmentSignificance = distanceSignificance * directionSignificance;
            nearestSegmentDistanceSignificance = distanceSignificance;
        }
    };

    testSegment(patchIt.segmentP(),localPatchPos.x());
    testSegment(patchIt.segmentQ(),localPatchPos.x());
    testSegment(patchIt.segmentR(),localPatchPos.y());
    testSegment(patchIt.segmentS(),localPatchPos.y());

  //  KIS_SAFE_ASSERT_RECOVER_RETURN(nearestSegment != mesh.endSegments());

    const qreal translationOffsetCoeff =
        qBound(0.0,
            linearReshapeFunc(1.0 - nearestSegmentDistanceSignificance,
                0.95, 0.75, 1.0, 0.0),
            1.0);
    const QPointF translationOffset = translationOffsetCoeff * offset;
    offset -= translationOffset;

    QPointF segmentOffset;

    if (nearestSegmentSignificance > 0) {
        segmentOffset = nearestSegmentSignificance * offset;
        offset -= segmentOffset;
    }

    const qreal alpha =
        1.0 - KisBezierUtils::curveProportionByParam(nearestSegment.p0(),
            nearestSegment.p1(),
            nearestSegment.p2(),
            nearestSegment.p3(),
            nearestSegmentParam, 0.1);

    const qreal coeffN1 =
        alpha > 0.5 ? std::max(0.0, linearReshapeFunc(alpha, 0.6, 0.75, 1.0, 0.0)) : 1.0;
    const qreal coeffN0 =
        alpha < 0.5 ? std::max(0.0, linearReshapeFunc(alpha, 0.25, 0.4, 0.0, 1.0)) : 1.0;

    smartMoveControl(mesh, nearestSegment.itP0().controlIndex(), offset * coeffN0, KisSmartMoveMeshControlMode::MoveSymmetricLock, 0);
    smartMoveControl(mesh, nearestSegment.itP3().controlIndex(), offset * coeffN1, KisSmartMoveMeshControlMode::MoveSymmetricLock, 0);
                 
    smartMoveControl(mesh, patchIt.nodeTopLeft().controlIndex(), translationOffset, KisSmartMoveMeshControlMode::MoveSymmetricLock, 0);
    smartMoveControl(mesh, patchIt.nodeTopRight().controlIndex(), translationOffset, KisSmartMoveMeshControlMode::MoveSymmetricLock, 0);
    smartMoveControl(mesh, patchIt.nodeBottomLeft().controlIndex(), translationOffset, KisSmartMoveMeshControlMode::MoveSymmetricLock,0);
    smartMoveControl(mesh, patchIt.nodeBottomRight().controlIndex(), translationOffset, KisSmartMoveMeshControlMode::MoveSymmetricLock, 0);

    offsetSegment(nearestSegment, nearestSegmentParam, segmentOffset);

    //QImage dstImage(srcImage.size(), srcImage.format());


    const QRect dstImageRect = mesh.dstBoundingRect().toAlignedRect();
    QImage dstImage(dstImageRect.size(), srcImage.format());
    auto itr = mesh.beginPatches();
    while (itr != mesh.endPatches()) {
        KisBezierPatch  patch = *itr;
        itr++;
        {
            //QImage dstImage(dstBoundsI.size(), srcImage.format());
          //  QImage dstImage(srcImage.size(), srcImage.format());
           // dstImage.fill(0);
            QRect rect = dstImage.rect();
            const QPoint srcQImageOffset;
            const QPoint dstQImageOffset;

            KisBezierTransformMesh::transformPatch(patch,
                srcQImageOffset, srcImage,
                dstImageRect.topLeft(), &dstImage);
            dstImage.save("d://temp//bezier_patch.png");
        }
    }
    int d = 1;

}


void bezier_test() {



    KisBezierTransformMesh my_mesh(QRect(0,0, 1920,1080), QSize(3,3));
   
    auto itr = my_mesh.beginPatches();
    while (itr != my_mesh.endPatches()) {
        KisBezierPatch  patch = *itr;
        itr++;
    }
    //KisBezierTransformMesh::makePatch(2, 2);

   // srcDev->convertFromQImage(srcImage, 0);
    QImage srcImage("d://temp//bezier_size.png");
    const QRect initialRect(srcImage.rect());

    KisBezierPatch patch;
    //patch.originalRect = initialRect;

    //patch.points[0] = initialRect.topLeft();
    //patch.points[1] = initialRect.topLeft() + QPointF(300, 30);
    //patch.points[2] = initialRect.topLeft() + QPointF(20, 300);
    //patch.points[3] = initialRect.topRight();
    //patch.points[4] = initialRect.topRight() + QPointF(-600, 30);
    //patch.points[5] = initialRect.topRight() + QPointF(-20, 300);
    //patch.points[6] = initialRect.bottomLeft();
    //patch.points[7] = initialRect.bottomLeft() + QPointF(300, 30);
    //patch.points[8] = initialRect.bottomLeft() + QPointF(20, -300);
    //patch.points[9] = initialRect.bottomRight();
    //patch.points[10] = initialRect.bottomRight() + QPointF(-300, 30);
    //patch.points[11] = initialRect.bottomRight() + QPointF(-20, -300);
    QImage dstImage(srcImage.size(), srcImage.format());











    patch.originalRect.setRect(0, 0, 336.5, 189.281);



    patch.points[0] = QPointF(0, 0);
    patch.points[1] = QPointF(33.65, 0);
    patch.points[2] = QPointF(0, 18.928);
    patch.points[3] = QPointF(336.5, 0);
    patch.points[4] = QPointF(185.075, 0);
    patch.points[5] = QPointF(336.5, 18.928);
    patch.points[6] = QPointF(0, 189.281);
    patch.points[7] = QPointF(33.65, 189.281);
    patch.points[8] = QPointF(0, 104.104);
    patch.points[9] = QPointF(241.5, 144.281);
    patch.points[10] = QPointF(90.075, 144.281);
    patch.points[11] = QPointF(241.5, 59.104);



    {


        //QImage dstImage(dstBoundsI.size(), srcImage.format());
      //  QImage dstImage(srcImage.size(), srcImage.format());
       // dstImage.fill(0);
        QRect rect = dstImage.rect();
        const QPoint srcQImageOffset;
        const QPoint dstQImageOffset;

        KisBezierTransformMesh::transformPatch(patch,
            srcQImageOffset, srcImage,
            dstQImageOffset, &dstImage);
        dstImage.save("d://temp//bezier_testx2.png");
    }






    patch.originalRect.setRect(336.5, 0 , 336.5, 189.281);



    patch.points[0] = QPointF(336.5, 0);
    patch.points[1] = QPointF(487.925, 0);
    patch.points[2] = QPointF(336.5, 18.928);
    patch.points[3] = QPointF(673, 0);
    patch.points[4] = QPointF(639.35, 0);
    patch.points[5] = QPointF(673, 18.928);
    patch.points[6] = QPointF(241.5 , 144.281);
    patch.points[7] = QPointF(392.925, 144.281);
    patch.points[8] = QPointF(241.5, 59.104);
    patch.points[9] = QPointF(673, 189.281);
    patch.points[10] =  QPointF(639.35, 189.281);
    patch.points[11] =  QPointF(673, 104.104);


    {


        //QImage dstImage(dstBoundsI.size(), srcImage.format());

        QRect rect = dstImage.rect();
        const QPoint srcQImageOffset;
        const QPoint dstQImageOffset;

        KisBezierTransformMesh::transformPatch(patch,
            srcQImageOffset, srcImage,
            dstQImageOffset, &dstImage);
        dstImage.save("d://temp//bezier_testx1.png");
    }





    patch.originalRect.setRect(0, 189.281, 336.5, 189.281);



    patch.points[0] = QPointF(0, 189.281);
    patch.points[1] = QPointF(33.65, 189.281);
    patch.points[2] = QPointF(0, 274.457);
    patch.points[3] = QPointF(241.5, 144.281);
    patch.points[4] = QPointF(90.075, 144.281);
    patch.points[5] = QPointF(241.5, 229.4578);
    patch.points[6] = QPointF(0, 378.5625);
    patch.points[7] = QPointF(33.65, 378.5625);
    patch.points[8] = QPointF(0, 359.6347);
    patch.points[9] = QPointF(336.5, 378.5625);
    patch.points[10] = QPointF(185.075, 378.5625);
    patch.points[11] = QPointF(336.5, 359.6343);


    {


        //QImage dstImage(dstBoundsI.size(), srcImage.format());

        QRect rect = dstImage.rect();
        const QPoint srcQImageOffset;
        const QPoint dstQImageOffset;

        KisBezierTransformMesh::transformPatch(patch,
            srcQImageOffset, srcImage,
            dstQImageOffset, &dstImage);
        dstImage.save("d://temp//bezier_testx3.png");
    }


    patch.originalRect.setRect(336.5, 189.281, 336.5, 189.281);



    patch.points[0] = QPointF(241.5, 144.281);
    patch.points[1] = QPointF(392.295, 144.281);
    patch.points[2] = QPointF(241.5, 229.457);
    patch.points[3] = QPointF(673, 189.281);
    patch.points[4] = QPointF(639.35, 189.281);
    patch.points[5] = QPointF(673, 274.4578);
    patch.points[6] = QPointF(336.5, 378.5625);
    patch.points[7] = QPointF(487.925, 378.5625);
    patch.points[8] = QPointF(336.5, 359.6343);
    patch.points[9] = QPointF(673.0, 378.5625);
    patch.points[10] = QPointF(639.35, 378.5625);
    patch.points[11] = QPointF(673, 359.63437);


    {


        //QImage dstImage(dstBoundsI.size(), srcImage.format());

        QRect rect = dstImage.rect();
        const QPoint srcQImageOffset;
        const QPoint dstQImageOffset;

        KisBezierTransformMesh::transformPatch(patch,
            srcQImageOffset, srcImage,
            dstQImageOffset, &dstImage);
        dstImage.save("d://temp//bezier_testx4.png");
    }


}

int main(){

    //move_patch();


    mesh_patch();

    QImage srcImage("d://temp//people.png");


//    QVector<QPointF> origPoints{ QPointF(0, 0), QPointF(srcImage.width() ,0) ,  QPointF(0, srcImage.height()),  QPointF(srcImage.width(), srcImage.height()),  QPointF(srcImage.width() / 2, srcImage.height() / 2) };
//    QVector<QPointF> transfPoints{ QPointF(0, 0), QPointF(srcImage.width() ,0) ,  QPointF(0, srcImage.height()),  QPointF(srcImage.width(), srcImage.height()),  QPointF(srcImage.width()/2, srcImage.height()) };
    
    
    
    QVector<QPointF> transfPoints{ QPointF(0, 0), QPointF(0, srcImage.height()), QPointF(srcImage.width() * 0.3, srcImage.height() / 2),  QPointF(srcImage.width() * 0.5, srcImage.height() / 2) };
    QVector<QPointF> origPoints{ QPointF(0, 0), QPointF(0, srcImage.height() ), QPointF(srcImage.width() * 0.3, srcImage.height() / 2), QPointF(srcImage.width() * 0.5, 0)                   };
    
    

    
    //QVector<QPointF> transfPoints{ QPointF(0,0), QPointF(srcImage.width(), 0) ,  QPointF(srcImage.width() / 2, 400) };
    QPointF  offset1(0, 0);
    QPointF  offset2(0, 0);

    //QVector<QPointF> origPoints;
    //QVector<QPointF> transfPoints;
    qreal alpha = 1.0;

    //QRectF bounds(srcImage.rect());

    //origPoints << bounds.topLeft();
    //origPoints << bounds.topRight();
    //origPoints << bounds.bottomRight();
    //origPoints << bounds.bottomLeft();

    //origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight());
    //origPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0);


    //transfPoints << bounds.topLeft();
    //transfPoints << bounds.bottomLeft() + 0.6 * (bounds.topRight() - bounds.bottomLeft());
    //transfPoints << bounds.topLeft() + 0.8 * (bounds.bottomRight() - bounds.topLeft());
    //transfPoints << bounds.bottomLeft() + QPointF(200, 0);

    //transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(40, 20);
    //transfPoints << 0.5 * (bounds.bottomLeft() + bounds.bottomRight()) + QPointF(-20, 0) + QPointF(-40, 20);




    srcImage.convertTo(QImage::Format_ARGB32);
    int format = srcImage.format();
    transformQImage(origPoints, transfPoints, 1.0, srcImage, offset1, &offset2 );

    return 0;
}